package com.hmall.common.advice;

import com.hmall.common.domain.R;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.exception.CommonException;
import com.hmall.common.exception.DbException;
import com.hmall.common.utils.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.util.NestedServletException;

import java.net.BindException;
import java.util.stream.Collectors;

/**
 * 全局异常处理类，捕获Controller层抛出的异常并统一处理
 * 通过@RestControllerAdvice注解自动扫描并注册为Spring组件
 */
@RestControllerAdvice
@Slf4j
public class CommonExceptionAdvice {

    /**
     * 处理数据库操作异常
     * @param e 数据库异常实例
     * @return 统一响应格式
     */
    @ExceptionHandler(DbException.class)
    public Object handleDbException(DbException e) {
        log.error("mysql数据库操作异常 -> ", e);
        return processResponse(e);
    }

    /**
     * 处理自定义业务异常
     * @param e 自定义异常实例
     * @return 统一响应格式
     */
    @ExceptionHandler(CommonException.class)
    public Object handleBadRequestException(CommonException e) {
        log.error("自定义异常 -> {} , 异常原因：{}  ",e.getClass().getName(), e.getMessage());
        log.debug("", e);
        return processResponse(e);
    }

    /**
     * 处理方法参数校验失败异常（JSR-303注解校验）
     * @param e 参数校验异常
     * @return 统一响应格式（包含所有校验失败信息）
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        // 提取所有校验错误信息并使用"|"连接
        String msg = e.getBindingResult().getAllErrors()
                .stream().map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining("|"));
        log.error("请求参数校验异常 -> {}", msg);
        log.debug("", e);
        return processResponse(new BadRequestException(msg));
    }

    /**
     * 处理请求参数绑定异常（如URL参数类型不匹配）
     * @param e 参数绑定异常
     * @return 统一响应格式
     */
    @ExceptionHandler(BindException.class)
    public Object handleBindException(BindException e) {
        log.error("请求参数绑定异常 ->BindException， {}", e.getMessage());
        log.debug("", e);
        return processResponse(new BadRequestException("请求参数格式错误"));
    }

    /**
     * 处理嵌套Servlet异常（通常由请求处理链中的错误引起）
     * @param e 嵌套Servlet异常
     * @return 统一响应格式
     */
    @ExceptionHandler(NestedServletException.class)
    public Object handleNestedServletException(NestedServletException e) {
        log.error("参数异常 -> NestedServletException，{}", e.getMessage());
        log.debug("", e);
        return processResponse(new BadRequestException("请求参数处理异常"));
    }

    /**
     * 处理未被其他处理器捕获的通用异常
     * @param e 通用异常
     * @return 统一响应格式（500内部错误）
     */
    @ExceptionHandler(Exception.class)
    public Object handleRuntimeException(Exception e) {
        log.error("其他异常 uri : {} -> ", WebUtils.getRequest().getRequestURI(), e);
        return processResponse(new CommonException("服务器内部异常", 500));
    }

    /**
     * 构建统一响应体
     * @param e 异常实例
     * @return HTTP响应实体（包含状态码和错误信息）
     */
    private ResponseEntity<R<Void>> processResponse(CommonException e){
        return ResponseEntity.status(e.getCode()).body(R.error(e));
    }
}
